import pandas as pd
import numpy as np
import datetime as dt
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import LabelEncoder
import gc
import warnings
warnings.filterwarnings('ignore')


# 自定义变量


def pre_labeler(origin, feature_list, dump, str2int_path):
    """
    :param origin: pd.DataFrame 原始数据
    :param feature_list: 准标签化特征
    :param dump:调用判断
        dump = True :建立映射并保存
        dump = False : 将映射值作用到原始文件
    :param str2int_path:str2int.pkl存储路径
    :return:标签化数据
    """
    if dump:
        le_name_mapping = {} # 需自行建立对象保存映射关系
        class_le = LabelEncoder()
        for i in range(len(feature_list)):
            class_le.fit(origin[feature_list[i]].values)
            le_name_mapping.update(
                {feature_list[i]: dict(zip(class_le.classes_, class_le.transform(class_le.classes_)))})
            origin[feature_list[i]] = class_le.fit_transform(origin[feature_list[i]].values)
        joblib.dump(le_name_mapping, str2int_path)
        return origin
    else:
        le_name_mapping = joblib.load(str2int_path)
        new_data = pd.DataFrame(columns=feature_list)
        for each in feature_list:
            new_data[each] = origin[each].map(lambda s: le_name_mapping[each].get(s))
        return new_data



def month_distance(data, month ,  coloumn_id = 'id'):
    '''
    距离某个特定月份的月份距离，经过尝试后弃用
    :param data:
    :param month:
    :param coloumn_id:
    :return:
    '''
    def _get_distance(x , month):
        if x > month:
            return 12 - (x - month)
        else:
            return month - x

    distance_name = str(month) + '_' + 'distance'
    data['id_time'] = data[coloumn_id].apply(lambda x : x[-1])
    data['id_'] = data[coloumn_id].apply(lambda x : x[0])
    data[distance_name] = data['id_time'].apply(lambda x : _get_distance(x.month ,month ))

    data_groupby = data[['id_','id_time',distance_name]].groupby(['id_','id_time']).median()
    return data_groupby

def if_month(data, month ,coloumn_id = 'id'):
    '''
    手动周期性特征

    :param data:
    :param month:
    :param coloumn_id:
    :return:
    '''
    if_month_name = 'if_' + str(month)
    data['id_time'] = data[coloumn_id].apply(lambda x: x[-1])
    data['id_'] = data[coloumn_id].apply(lambda x: x[0])
    data[if_month_name] = data['id_time'].apply(lambda x: 1 if x.month == month else 0)

    data_groupby = data[['id_', 'id_time', if_month_name]].groupby(['id_', 'id_time']).median()
    return data_groupby



def gen_exog_diff_trend(val_train,
                        val_train_diff,
                        val_train_diffm,
                        val_train_trend_df,
                        asccode,
                        period,
                        cycle):

    if cycle != 0 and cycle != 12 and cycle != 24 :
        raise ValueError('incorrect cycle input!!')

    year_month_iter = val_train.columns.tolist()
    feature_gen_table = pd.DataFrame()
    for one_ym_index, one_ym in enumerate(year_month_iter):
        print('now processing..:', one_ym)
        diff_ = val_train_diff.iloc[:, one_ym_index:]
        diffm_ = val_train_diffm.iloc[:, one_ym_index:]
        trend_ = val_train_trend_df.iloc[:, one_ym_index:]
        train_ = val_train.iloc[:, one_ym_index:]

        feature_gen = pd.DataFrame()

        for one_range in period:
            m_start, m_end = one_range
            m_start += cycle
            m_end += cycle

            c = 'median_%d_%d' % (m_start, m_end)
            cm = 'mean_%d_%d' % (m_start, m_end)
            cmax = 'max_%d_%d' % (m_start, m_end)
            cmin = 'min_%d_%d' % (m_start, m_end)

            ct = 'median_trend_%d_%d' % (m_start, m_end)
            ctm = 'mean_trend_%d_%d' % (m_start, m_end)
            ctmax = 'max_trend_%d_%d' % (m_start, m_end)
            ctmin = 'min_trend_%d_%d' % (m_start, m_end)

            cd = 'median_diff_%d_%d' % (m_start, m_end)
            cdmax = 'median_diffmax_%d_%d' % (m_start, m_end)
            cdmin = 'median_diffmin_%d_%d' % (m_start, m_end)

            cd12m = 'median_diff12m_%d_%d' % (m_start, m_end)
            cdmax12m = 'median_diff12mmax_%d_%d' % (m_start, m_end)
            cdmin12m = 'median_diff12mmin_%d_%d' % (m_start, m_end)

            feature_gen[c] = train_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[cm] = train_.iloc[:, m_start: m_end].mean(axis=1, skipna=False)
            feature_gen[cmax] = train_.iloc[:, m_start: m_end].max(axis=1, skipna=False)
            feature_gen[cmin] = train_.iloc[:, m_start: m_end].max(axis=1, skipna=False)

            feature_gen[ct] = trend_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[ctm] = trend_.iloc[:, m_start: m_end].mean(axis=1, skipna=False)
            feature_gen[ctmax] = trend_.iloc[:, m_start: m_end].max(axis=1, skipna=False)
            feature_gen[ctmin] = trend_.iloc[:, m_start: m_end].max(axis=1, skipna=False)

            feature_gen[cd] = diff_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[cdmax] = diff_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[cdmin] = diff_.iloc[:, m_start: m_end].median(axis=1, skipna=False)

            feature_gen[cd12m] = diffm_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[cdmax12m] = diffm_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
            feature_gen[cdmin12m] = diffm_.iloc[:, m_start: m_end].median(axis=1, skipna=False)
        feature_gen['Year_month'] = one_ym
        feature_gen_table = pd.concat([feature_gen_table, feature_gen], axis=0)

    feature_gen_table['asccode'] = asccode * (one_ym_index + 1)

    gc.collect()

    return feature_gen_table



def gen_X_y_selfexog(data_total, features_tables):


    list2melt = data_total.columns.tolist()
    try:
        list2melt.remove('city_label')
        list2melt.remove('province_label')
        list2melt.remove('area_label')

        list2melt.remove('city')
        list2melt.remove('province')
        list2melt.remove('area')
    except:
        pass

    data_total_melt = data_total[list2melt].melt(id_vars = 'asc_code')
    data_total_melt.rename(columns = {'variable': 'Year_month'} , inplace = True)

    data_total_melt['date'] = data_total_melt['Year_month'].apply(lambda x : dt.datetime.strptime(x +'-01' , '%Y-%m-%d'))
    data_total_melt['date_shift_manual'] = data_total_melt['date'].apply(lambda x : x - relativedelta(months=relativedelta_month))
    data_total_melt['date_shift_manual'] = data_total_melt['date_shift_manual'].apply(lambda x : dt.datetime.strftime(x , '%Y-%m-%d'))
    data_total_melt['date_shift_manual'] = data_total_melt['date_shift_manual'].apply(lambda x : x.replace('-01',''))

    gen_X_y = data_total_melt.merge(features_tables , on = ['asccode','Year_month'] , how = 'inner')
    gen_X_y = gen_X_y.merge(data_total[['asc_code','city_label' , 'province_label' , 'area_label']],
                            left_on = 'asccode', right_on = 'asccode', how = 'left')
    return gen_X_y






